#include "iomanager.h"
#include "level.h"
#include "point.h"
#include "line.h"
#include <QtXml>
#include <QDebug>

IOManager::IOManager(const QString &fileName) :
    fileName(fileName), levelsFile(NULL)
{
    QByteArray init("Crosslines init key!");
    QByteArray key = QCryptographicHash::hash(init, QCryptographicHash::Md5);
    byteKey = new byte[key.size()];
    charToByte(key.data(), byteKey, key.size());

}

//void IOManager::saveLevel(const Level *const level)
//{
//    QDomDocument doc("LEVELS");
//    QDomElement root = doc.createElement("levels");
//    doc.appendChild(root);

//    QDomElement tag = doc.createElement("Greeting");
//    root.appendChild(tag);

//    QDomText t = doc.createTextNode("Hello World");
//    tag.appendChild(t);

//    QString xml = doc.toString();
//    qDebug() << xml;
//}

void IOManager::saveLevels(const QLinkedList<Level *> &levels)
{
    qDebug() << levels.size();
    QDomDocument doc("LEVELS");
    QDomElement root = doc.createElement("levels");

    QLinkedList<Level*>::const_iterator it;

    for (it=levels.begin(); it!=levels.end(); it++)
    {
        QDomElement levelElement = doc.createElement("level");
        levelElement.setAttribute("id", (*it)->getId());
        levelElement.setAttribute("name", (*it)->getName());
        levelElement.setAttribute("lock", (*it)->isLocked() ? 1 : 0);

        QDomElement pointsElement = doc.createElement("points");
        QLinkedList<Point*> pl = (*it)->getPointsList();
        QHash<Point*, int> pointIdMap;
        int i;
        QLinkedList<Point*>::const_iterator pit;
        for (i=1, pit=pl.begin(); pit!=pl.end(); i++, pit++)
        {
            QDomElement pointElement = doc.createElement("point");
            pointElement.setAttribute("id", i);
//            Point* p = *pit;p->pos().x()
            pointElement.setAttribute("x", (*pit)->rect().center().x());
            pointElement.setAttribute("y", (*pit)->rect().center().y());
            pointIdMap[*pit] = i;
            pointsElement.appendChild(pointElement);
        }
        levelElement.appendChild(pointsElement);

        QDomElement linesElement = doc.createElement("lines");
        QLinkedList<Line*> ll = (*it)->getLinesList();
        QLinkedList<Line*>::const_iterator lit;
        for (lit=ll.begin(); lit!=ll.end(); lit++)
        {
            QDomElement lineElement = doc.createElement("line");
            lineElement.setAttribute("p1", pointIdMap[(*lit)->getStartPoint()]);
            lineElement.setAttribute("p2", pointIdMap[(*lit)->getEndPoint()]);
            lineElement.setAttribute("r", (*lit)->getColor().red());
            lineElement.setAttribute("g", (*lit)->getColor().green());
            lineElement.setAttribute("b", (*lit)->getColor().blue());
            linesElement.appendChild(lineElement);
        }
        levelElement.appendChild(linesElement);

        root.appendChild(levelElement);
    }
    doc.appendChild(root);

    data = QByteArray().append(doc.toString());
    encryptData();
    openLevelFile();
    levelsFile->write(data);
    closeLevelFile();
}

Level* IOManager::loadLevel(qint32 id)
{
    QLinkedList<Level*> levels;
    loadLevels(levels);
    QLinkedList<Level*>::const_iterator it;
    for (it=levels.begin(); it!=levels.end(); it++)
        if ( (*it)->getId() == id )
            return (*it);
    return NULL;
}

void IOManager::loadLevels(QLinkedList<Level *> &levels)
{
    QDomDocument doc("LEVELS");
    openLevelFile();
    data = levelsFile->readAll();
    decryptData();
    doc.setContent(data);
    closeLevelFile();

    QDomElement root = doc.documentElement();
    QDomElement levelsElement = root.firstChildElement("level");
    while (!levelsElement.isNull()) {
        qint32 id = levelsElement.attribute("id").toInt();
        QString name = levelsElement.attribute("name");
        Level *level = new Level(id, name);

        qint32 lock = levelsElement.attribute("lock").toInt();
        level->setLocked(lock==0?false:true);
        levels.push_back(level);

        QDomElement pointsElement = levelsElement.firstChildElement("points");
        if (pointsElement.isNull())
            continue;
        QHash<int, Point*> pointIdMap;
        QDomElement pointElement = pointsElement.firstChildElement("point");

        while (!pointElement.isNull()) {
            Point *point = new Point(QPointF(pointElement.attribute("x").toFloat(),
                                             pointElement.attribute("y").toFloat()));
            pointIdMap[pointElement.attribute("id").toInt()] = point;
            level->addPoint(point);
            pointElement = pointElement.nextSiblingElement("point");
        }

        QDomElement linesElement = levelsElement.firstChildElement("lines");
        if (linesElement.isNull())
            continue;
        QDomElement lineElement = linesElement.firstChildElement("line");
        while (!lineElement.isNull()) {
            qint32 p1Id = lineElement.attribute("p1").toInt();
            qint32 p2Id = lineElement.attribute("p2").toInt();
            QColor color(lineElement.attribute("r").toInt(),
                         lineElement.attribute("g").toInt(),
                         lineElement.attribute("b").toInt());
            Line* line = new Line(pointIdMap[p1Id]);
            line->setDestnation(pointIdMap[p2Id]);
            line->setColor(color);
            level->addLine(line);

            lineElement = lineElement.nextSiblingElement("line");
        }


        levelsElement = levelsElement.nextSiblingElement("level");
    }
}

void IOManager::openLevelFile()
{
    if (levelsFile == NULL)
        levelsFile = new QFile(fileName);
    if (levelsFile->isOpen())
        return;
    if (!levelsFile->open(QIODevice::ReadWrite))
        return;
}

void IOManager::closeLevelFile()
{

    if (levelsFile == NULL || !levelsFile->isOpen())
        return;
    levelsFile->close();
}


void IOManager::encryptData()
{
    byte* byteData = new byte[data.size()];
    byte* cipherText = new byte[data.size()];
    charToByte(data.data(), byteData, data.size());
    encryptBlock(byteData, cipherText, data.size());
    char* eData = new char[data.size()];
    byteToChar(cipherText, eData, data.size());
    data = QByteArray(eData, data.size());
    delete byteData;
    delete cipherText;
    delete eData;
}

void IOManager::decryptData()
{

    byte* byteData = new byte[data.size()];
    byte* cipherText = new byte[data.size()];
    charToByte(data.data(), cipherText, data.size());
    decryptBlock(cipherText, byteData, data.size());
    char* eData = new char[data.size()];
    byteToChar(byteData, eData, data.size());
    data = QByteArray(eData, data.size());
    delete byteData;
    delete cipherText;
    delete eData;
}

void IOManager::encryptBlock(byte *plaintext, byte *ciphertext, unsigned int count)
{
    for (unsigned int i=0; i<count; i++)
    {
        ciphertext[i] = plaintext[i] ^ byteKey[i%16];
    }
}

void IOManager::decryptBlock(byte *ciphertext, byte *plaintext, unsigned int count)
{
    for (unsigned int i=0; i<count; i++)
    {
        plaintext[i] = ciphertext[i] ^ byteKey[i%16];
    }
}

void IOManager::charToByte(char* chars, byte* bytes, unsigned int count)
{
    for(unsigned int i = 0; i < count; i++)
        bytes[i] = (byte)chars[i];
}

void IOManager::byteToChar(byte* bytes, char* chars, unsigned int count)
{
    for(unsigned int i = 0; i < count; i++)
         chars[i] = (char)bytes[i];
}

void IOManager::privateLoadLevels(QLinkedList<Level *> &levels)
{

}
